package lazy.sql;

import java.lang.reflect.Method;
import java.util.*;
import ebti.db.annotation.Column;
import lazy2.util.StringUtil;

public class Where {
	private Map<String,List<LogicColumnOperatorValue>> whereColumns = new LinkedHashMap<String, List<LogicColumnOperatorValue>>();
	public void add(LogicColumnOperatorValue columnLogicValue) {
		if(columnLogicValue==null) return;
		String key = columnLogicValue.getColumn();
		List<LogicColumnOperatorValue> l = this.whereColumns.get(key);
		if(l==null) {
			l = new ArrayList<LogicColumnOperatorValue>();
			this.whereColumns.put(key, l);
		}
		l.add(columnLogicValue);
	}
	public void replace(LogicColumnOperatorValue columnLogicValue, int index) {
		if(columnLogicValue!=null) {
			List<LogicColumnOperatorValue> l = this.whereColumns.get(columnLogicValue.getColumn());
			if(l!=null && l.size()>index) {
				l.set(index, columnLogicValue);
			}
		}
	}
	
	public void replace(LogicColumnOperatorValue columnLogicValue) {replace(columnLogicValue, 0);}
	public void remove(String column) {
		if(StringUtil.hasText(column)) whereColumns.remove(column.toUpperCase());
	}
	private boolean appendCondition(StringBuilder sql, List<Object> values, boolean isAppendSqlLogic, LogicColumnOperatorValue c) {
		if(sql==null || values==null || c==null) return false;
		//-----------------------------------------------------------
		Object value = c.getValue();
		if(value==null || !StringUtil.hasText(value.toString())) return false;
		//-----------------------------------------------------------
		sql.append(" ");
		if(isAppendSqlLogic) {
			if(c.getSqlLogic()==null) sql.append(SqlLogic.AND.name()).append(" ");
			else sql.append(c.getSqlLogic().name()).append(" ");
		}
		if(StringUtil.hasText(c.getTableAlias())) sql.append(c.getTableAlias()).append(".");
		sql.append(c.getColumn());
		sql.append(c.getSqlOperator().toOperator());
		if(c.getSqlOperator().equals(SqlOperator.IN)) {
			sql.append(" ");
			sql.append(c.getValue());
		} else {
			sql.append("?");
			if(value instanceof java.util.Date) {
				values.add(value);
			} else {
				switch (c.getSqlOperator()) {
					case LEFT_LIKE:
						values.add("%".concat(value.toString()));
						break;
					case LIKE:
						values.add("%".concat(value.toString()).concat("%"));
						break;
					case RIGHT_LIKE:
						values.add(value.toString().concat("%"));
						break;
					default:
						values.add(value);
						break;
				}
			}
		}
		return true;
	}
	public WhereSqlValue toWhereSqlValue() {
		StringBuilder sql = new StringBuilder("");
		List<Object> values = new ArrayList<Object>();
		boolean isAppendSqlLogic = false;
		for(Map.Entry<String,List<LogicColumnOperatorValue>> e:whereColumns.entrySet()) {
			for(LogicColumnOperatorValue c:e.getValue()) {
				List<LogicColumnOperatorValue> subSelectList = c.getSubSelectList();
				if(subSelectList!=null && subSelectList.size()>0) {
					sql.append(" ");
					if(isAppendSqlLogic) {
						if(c.getSqlLogic()==null) sql.append(SqlLogic.AND.name()).append(" ");
						else sql.append(c.getSqlLogic().name()).append(" ");
					}
					sql.append("(");
					boolean isAppendSubSelectSqlLogic = appendCondition(sql, values, false, c);
					for(int i=0;i<subSelectList.size();i++) {
						if(appendCondition(sql, values, isAppendSubSelectSqlLogic, subSelectList.get(i))) isAppendSubSelectSqlLogic = true;
					}
					sql.append(")");
				} else {
					if(appendCondition(sql, values, isAppendSqlLogic, c)) isAppendSqlLogic = true;
				}				
			}
		}
		if(isAppendSqlLogic) {
			sql.insert(0, " WHERE");
		}
		return new WhereSqlValue(sql.toString(), values);
	}
	public static LogicColumnOperatorValue createLogicColumnOperatorValueWithoutValue(Class<?> cls, String property) {
		if(cls!=null && StringUtil.hasText(property)) {
			LogicColumnOperatorValue l = null;
			try {
				Method method = cls.getMethod(StringUtil.toGetter(property));
				Column col = method.getAnnotation(Column.class);
				if(col!=null) {
					l = new LogicColumnOperatorValue(SqlLogic.AND, col.value(), SqlOperator.EQUAL, null);
				} else {
					l = new LogicColumnOperatorValue(SqlLogic.AND, property, SqlOperator.EQUAL, null);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			return l;
		}
		return null;
	}
	public static LogicColumnOperatorValue createLogicColumnOperatorValue(Object bean, String property) {
		if(bean!=null && StringUtil.hasText(property)) {
			LogicColumnOperatorValue l = null;
			try {
				Class<?> cls = bean.getClass();
				Method method = cls.getMethod(StringUtil.toGetter(property));
				Column col = method.getAnnotation(Column.class);
				if(col!=null) {
					l = new LogicColumnOperatorValue(SqlLogic.AND, col.value(), SqlOperator.EQUAL, method.invoke(bean));
				} else {
					l = new LogicColumnOperatorValue(SqlLogic.AND, property, SqlOperator.EQUAL, method.invoke(bean));
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			return l;
		}
		return null;
	}
	public static <T> Where getDefaultWhere(T t, String tableAlias) {
		Where where = new Where();
		if(t!=null) {
			String methodName;
			LogicColumnOperatorValue l;
			for(Method m:t.getClass().getMethods()) {
				methodName = m.getName();
				l = null;
				if(!methodName.startsWith("get") || methodName.equals("getClass")) continue;
				try {
					if(m.isAnnotationPresent(DefaultWhere.class)) {
						l = new LogicColumnOperatorValue(m.getAnnotation(DefaultWhere.class), StringUtil.toProperty(methodName), m.invoke(t));
						if(StringUtil.hasTextIgnoreWhitSpaces(tableAlias)) l.setTableAlias(tableAlias);
					} else if(m.isAnnotationPresent(Column.class)) {
						l = new LogicColumnOperatorValue(SqlLogic.AND, tableAlias, m.getAnnotation(Column.class).value(), SqlOperator.EQUAL, m.invoke(t));
					} else {
						if(!m.isAnnotationPresent(ebti.db.annotation.NotColumn.class))
							l = new LogicColumnOperatorValue(SqlLogic.AND, tableAlias, StringUtil.toProperty(methodName), SqlOperator.EQUAL, m.invoke(t));
					}
					if(l!=null) where.add(l);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		return where;
	}
	public static <T> Where getDefaultWhere(T t) {return getDefaultWhere(t, null);}
}